﻿using Microsoft.Xna.Framework;
using System;

namespace Organized
{
    // Basic camera class
    public class Camera : Component, I3DComponent
    {
        // Internal values
        Vector3 position = Vector3.Zero;
        Matrix rotationMatrix = Matrix.Identity;
        Vector3 target = new Vector3(0,0,-1);
        Vector3 up = Vector3.Up;
        Matrix view;
        Matrix projection;
      
        Matrix fprojection;

        public float FOV = MathHelper.PiOver4;
        // Vector the camera is looking at
        public virtual Vector3 Target
        {
            get { return target; }
            set { target = value; }
        }

        // The View and Projection matrices commonly used for rendering
        public virtual Matrix View
        {
            get { return view; }
            set { view = value; } 
        }

        public virtual Matrix Projection
        {
            get { return projection; }
            set { projection = value; }
        }

        public virtual Matrix FrustumProjection{
            get { return fprojection;}
            set { fprojection = value;}
        }

        public virtual Vector3 Up
        {
            get { return up; }
            set { up = value; }
        }

        // Public I3DComponent values
        public virtual Vector3 Position
        {
            get { return position;}
            set { position = value;}
        }

        public virtual Vector3 Scale
        {
            get { return Vector3.One;}
            set {  } 
        }

        public Vector3 EulerRotation
        {
            get { return MathUtil.MatrixToVector3(Rotation); }
            set { rotationMatrix = MathUtil.Vector3ToMatrix(value);}
        }

        // the rotation matrix used by the camera and the current up vector
        public virtual Matrix Rotation
        {
            get { return rotationMatrix; }
            set { rotationMatrix = value; }
        }

        public virtual BoundingBox BoundingBox
        {
            get { return new BoundingBox(Position - Vector3.One, Position + Vector3.One);}
        }

        public BoundingFrustum BoundingFrustum;

        

        //Constructors
        public Camera(GameScreen Parent) : base(Parent) { }
  

        //update the camera
        public override void  Update()
        {
            //Calculate the direction from the position to the target, normalize
            Vector3 newForward = Target - Position;
            newForward.Normalize();

            // Set the rotation matrix's forward to his vector
            Matrix rotationMatrixCopy = this.Rotation;
            rotationMatrixCopy.Forward = newForward;

            // Save a copy of "up" 
            Vector3 referenceVector = Vector3.Up;

            // if camera is pointed parallel to the Y Axis, change the reference vector to the forward axis (Z)
            if(rotationMatrixCopy.Forward.Y == referenceVector.Y || rotationMatrixCopy.Forward.Y == -referenceVector.Y)
                referenceVector = Vector3.Backward;

            // Calculate the other parts of the rotation matrix
            rotationMatrixCopy.Right = Vector3.Cross(this.Rotation.Forward, referenceVector);
            rotationMatrixCopy.Up = Vector3.Cross(this.Rotation.Right, this.Rotation.Forward);
            this.Rotation = rotationMatrixCopy;

            // find the new up
            Up = Rotation.Up;

            //recalculate view and projection using the new position, target, and up
            View = Matrix.CreateLookAt(Position, Target, Up);
            Projection = MathUtil.CreateProjectionMatrix(FOV);
            FrustumProjection = MathUtil.CreateProjectionMatrix(FOV, 5000.0f);
        }
    }
            
}
        

                 